﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.Extensions.Logging;
using NetModular.Lib.Cache.Abstractions;
using NetModular.Lib.Config.Abstractions;
using NetModular.Lib.Config.Abstractions.Impl;
using NetModular.Lib.Module.Abstractions;
using NetModular.Module.Admin.Domain.AuditInfo;
using NetModular.Module.Admin.Domain.Module;
using NetModular.Module.Admin.Domain.Permission;
using NetModular.Module.Admin.Infrastructure;
using NetModular.Module.Admin.Infrastructure.Repositories;
using NetModular.Module.Admin.Application.ModuleService.ResultModels;
using NetModular.Module.Admin.Application.ModuleService.ViewModels;
using NetModular.Module.Admin.Domain.Class;
using NetModular.Module.Admin.Domain.ClassMethod;
using NetModular.Module.Admin.Domain.Enum;
using NetModular.Module.Admin.Domain.EnumItem;
using NetModular.Module.Admin.Domain.ModelProperty;
using NetModular.Module.Admin.Domain.Module.Models;
using NetModular.Module.Admin.Domain.Property;
using NetModular.Module.Admin.Infrastructure.NuGet;
using NetModular.Module.Admin.Infrastructure.Templates.Default;
using NetModular.Module.Admin.Infrastructure.Templates.Models;

namespace NetModular.Module.Admin.Application.ModuleService
{
    public class ModuleService : IModuleService
    {
        private readonly IModuleRepository _repository;
        private readonly IAuditInfoRepository _auditInfoRepository;
        private readonly IModuleCollection _moduleCollection;
        private readonly AdminDbContext _dbContext;
        private readonly ILogger _logger;
        private readonly ICacheHandler _cacheHandler;
        private readonly IPermissionRepository _permissionRepository;

        private readonly IMapper _mapper;
        private readonly IClassRepository _classRepository;
        private readonly IPropertyRepository _propertyRepository;
        private readonly IEnumRepository _enumRepository;
        private readonly IEnumItemRepository _enumItemRepository;
        private readonly IModelPropertyRepository _modelPropertyRepository;
        private readonly IClassMethodRepository _classMethodRepository;
        private readonly NuGetHelper _nugetHelper;
        private readonly IConfigProvider _configProvider;

        public ModuleService(IModuleRepository repository, IModuleCollection moduleCollection, AdminDbContext dbContext, ILogger<ModuleService> logger, IAuditInfoRepository auditInfoRepository, ICacheHandler cacheHandler, IPermissionRepository permissionRepository, IMapper mapper, IClassRepository classRepository, IPropertyRepository propertyRepository, IEnumRepository enumRepository, IEnumItemRepository enumItemRepository, IModelPropertyRepository modelPropertyRepository, IClassMethodRepository classMethodRepository, NuGetHelper nugetHelper, IConfigProvider configProvider)
        {
            _repository = repository;
            _moduleCollection = moduleCollection;
            _dbContext = dbContext;
            _logger = logger;
            _auditInfoRepository = auditInfoRepository;
            _cacheHandler = cacheHandler;
            _permissionRepository = permissionRepository;

            _mapper = mapper;
            _classRepository = classRepository;
            _propertyRepository = propertyRepository;
            _enumRepository = enumRepository;
            _enumItemRepository = enumItemRepository;
            _modelPropertyRepository = modelPropertyRepository;
            _classMethodRepository = classMethodRepository;
            _nugetHelper = nugetHelper;
            _configProvider = configProvider;
        }

        public async Task<IResultModel> Query()
        {
            var list = await _repository.GetAllAsync();
            return ResultModel.Success(list);
        }

        public async Task<IResultModel> Sync(List<PermissionEntity> permissions)
        {
            using var uow = _dbContext.NewUnitOfWork();

            #region ==同步模块信息==

            var modules = _moduleCollection.Select(m => new ModuleEntity
            {
                Number = m.Id,
                Name = m.Name,
                Code = m.Code,
                Icon = m.Icon,
                Version = m.Version,
                Remarks = m.Description
            });

            _logger.LogDebug("Sync Module Info");

            foreach (var module in modules)
            {
                if (!await _repository.Exists(module, uow))
                {
                    if (!await _repository.AddAsync(module))
                    {
                        return ResultModel.Failed();
                    }
                }
                else
                {
                    if (!await _repository.UpdateByCode(module))
                    {
                        return ResultModel.Failed();
                    }
                }
            }

            #endregion

            #region ==同步权限信息=

            if (permissions != null && permissions.Any())
            {
                _logger.LogDebug("Sync Permission Info");

                //先清除已有权限信息
                if (await _permissionRepository.ClearAsync(uow))
                {
                    foreach (var permission in permissions)
                    {
                        if (!await _permissionRepository.AddAsync(permission, uow))
                            return ResultModel.Failed("同步失败");
                    }

                    //删除所有账户的权限缓存
                    await _cacheHandler.RemoveByPrefixAsync(CacheKeys.ACCOUNT_PERMISSIONS);
                }
            }

            #endregion

            uow.Commit();

            return ResultModel.Success();
        }

        public IResultModel Select()
        {
            var list = _moduleCollection.OrderBy(m => m.Id).Select(m => new OptionResultModel
            {
                Label = m.Name,
                Value = m.Code,
                Data = new
                {
                    m.Id,
                    m.Code,
                    m.Name,
                    m.Icon,
                    m.Description
                }
            }).ToList();

            return ResultModel.Success(list);
        }

        public async Task<bool> SyncApiRequestCount()
        {
            var counts = await _auditInfoRepository.QueryCountByModule();
            if (!counts.Any())
                return true;

            using var uow = _dbContext.NewUnitOfWork();

            var tasks = new List<Task>();
            foreach (var option in counts)
            {
                tasks.Add(_repository.UpdateApiRequestCount(option.Label, option.Value.ToLong(), uow));
            }

            await Task.WhenAll(tasks);

            uow.Commit();

            return true;
        }

        #region 代码生成器代码

        public async Task<IResultModel> Query(ModuleQueryModel model)
        {
            var result = new QueryResultModel<ModuleEntity>
            {
                Rows = await _repository.Query(model),
                Total = model.TotalCount
            };
            return ResultModel.Success(result);
        }

        public async Task<IResultModel> Add(ModuleAddModel model)
        {
            var entity = _mapper.Map<ModuleEntity>(model);
            var result = await _repository.AddAsync(entity);
            return ResultModel.Result(result);
        }

        public async Task<IResultModel> Delete(Guid id)
        {
            var entity = await _repository.GetAsync(id);
            if (entity == null)
                return ResultModel.NotExists;

            using (var uow = _dbContext.NewUnitOfWork())
            {
                var result = await _repository.SoftDeleteAsync(id, uow);
                if (result)
                {
                    result = await _classRepository.DeleteByModule(id, uow);
                    if (result)
                    {
                        result = await _propertyRepository.DeleteByModule(id, uow);
                        if (result)
                        {
                            result = await _modelPropertyRepository.DeleteByModule(id, uow);
                            if (result)
                            {
                                uow.Commit();
                                return ResultModel.Success();
                            }
                        }
                    }
                }
            }

            return ResultModel.Failed();
        }

        public async Task<IResultModel> Edit(Guid id)
        {
            var entity = await _repository.GetAsync(id);
            if (entity == null)
                return ResultModel.NotExists;

            var model = _mapper.Map<ModuleUpdateModel>(entity);
            return ResultModel.Success(model);
        }

        public async Task<IResultModel> Update(ModuleUpdateModel model)
        {
            var entity = await _repository.GetAsync(model.Id);
            if (entity == null)
                return ResultModel.NotExists;

            _mapper.Map(model, entity);

            var result = await _repository.UpdateAsync(entity);
            return ResultModel.Result(result);
        }

        public Task<IResultModel<ModuleBuildCodeResultModel>> BuildCode(ModuleBuildCodeModel model)
        {
            return BuildCode(model.Id);
        }

        public async Task<IResultModel<ModuleBuildCodeResultModel>> BuildCode(Guid moduleId, IList<ClassEntity> classList = null)
        {
            var result = new ResultModel<ModuleBuildCodeResultModel>();

            var module = await _repository.GetAsync(moduleId);
            if (module == null)
                return result.Failed("模块不存在");

            //创建模块生成对象
            var moduleBuildModel = _mapper.Map<ModuleBuildModel>(module);

            var config = _configProvider.Get<CodeGeneratorConfig>();
            moduleBuildModel.Prefix = config.Prefix;
            moduleBuildModel.UiPrefix = config.UiPrefix;

            var id = Guid.NewGuid().ToString();
            var rootPath = config.BuildCodePath;
            if (rootPath.IsNull())
            {
                var pathConfig = _configProvider.Get<PathConfig>();
                rootPath = Path.Combine(pathConfig.TempPath, "CodeGenerator/BuildCode");
            }

            var moduleFullName = $"{config.Prefix}.Module.{module.Code}";
            var buildModel = new TemplateBuildModel
            {
                RootPath = Path.Combine(rootPath, id, moduleFullName),
                NuGetPackageVersions = _nugetHelper.GetVersions()
            };

            if (classList == null)
            {
                //如果classList参数为null，表示生成整个解决方案
                buildModel.GenerateSln = true;
                classList = await _classRepository.QueryAllByModule(module.Id);
            }

            foreach (var classEntity in classList)
            {
                var classBuildModel = _mapper.Map<ClassBuildModel>(classEntity);
                var propertyList = await _propertyRepository.QueryByClass(classEntity.Id);
                if (propertyList.Any())
                {
                    //查询属性
                    foreach (var propertyEntity in propertyList)
                    {
                        var propertyBuildModel = _mapper.Map<PropertyBuildModel>(propertyEntity);

                        //如果属性类型是枚举，查询枚举信息
                        if (propertyEntity.Type == PropertyType.Enum && propertyEntity.EnumId.NotEmpty())
                        {
                            var enumEntity = await _enumRepository.GetAsync(propertyEntity.EnumId);
                            propertyBuildModel.Enum = new EnumBuildModel
                            {
                                Name = enumEntity.Name,
                                Remarks = enumEntity.Remarks
                            };

                            var enumItemList = await _enumItemRepository.QueryByEnum(propertyEntity.EnumId);
                            propertyBuildModel.Enum.ItemList = enumItemList.Select(m => new EnumItemBuildModel
                            {
                                Name = m.Name,
                                Remarks = m.Remarks,
                                Value = m.Value
                            }).ToList();
                        }

                        classBuildModel.PropertyList.Add(propertyBuildModel);
                    }
                }

                var modelPropertyList = await _modelPropertyRepository.QueryByClass(classEntity.Id);

                if (modelPropertyList.Any())
                {
                    foreach (var propertyEntity in modelPropertyList)
                    {
                        var modelPropertyBuildModel = _mapper.Map<ModelPropertyBuildModel>(propertyEntity);
                        //如果属性类型是枚举，查询枚举信息
                        if (propertyEntity.Type == PropertyType.Enum && propertyEntity.EnumId.NotEmpty())
                        {
                            var enumEntity = await _enumRepository.GetAsync(propertyEntity.EnumId);
                            modelPropertyBuildModel.Enum = new EnumBuildModel
                            {
                                Name = enumEntity.Name,
                                Remarks = enumEntity.Remarks
                            };

                            var enumItemList = await _enumItemRepository.QueryByEnum(propertyEntity.EnumId);
                            modelPropertyBuildModel.Enum.ItemList = enumItemList.Select(m => new EnumItemBuildModel
                            {
                                Name = m.Name,
                                Remarks = m.Remarks,
                                Value = m.Value
                            }).ToList();
                        }

                        classBuildModel.ModelPropertyList.Add(modelPropertyBuildModel);
                    }
                }

                classBuildModel.Method = await _classMethodRepository.GetByClass(classEntity.Id);

                moduleBuildModel.ClassList.Add(classBuildModel);
            }

            buildModel.Module = moduleBuildModel;

            var builder = new DefaultTemplateBuilder();
            builder.Build(buildModel);

            var sourceDir = Path.Combine(rootPath, id);
            var outputFile = Path.Combine(rootPath, id + ".zip");
            ZipFile.CreateFromDirectory(sourceDir, outputFile);

            //删除临时文件
            Directory.Delete(sourceDir, true);

            var resultModel = new ModuleBuildCodeResultModel
            {
                Id = id,
                Name = moduleBuildModel.Name + ".zip",
                ZipPath = outputFile
            };

            return result.Success(resultModel);
        }
        #endregion
    }
}
